package cn.icanci.rec.common.utils;

import java.lang.reflect.Type;
import java.nio.charset.CharsetDecoder;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.serializer.SimpleDateFormatSerializer;

/**
 * @author icanci
 * @since 1.0 Created in 2022/11/13 20:24
 */
public class FastJsonUtils {

    /** 序列化属性 */
    private static final SerializerFeature[] SERIALIZER_FEATURES            = { SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty,
                                                                                SerializerFeature.WriteNullNumberAsZero, SerializerFeature.WriteNullBooleanAsFalse,
                                                                                SerializerFeature.WriteNullStringAsEmpty, SerializerFeature.SortField,
                                                                                SerializerFeature.SkipTransientField };

    /** 空值序列化属性 */
    private static final SerializerFeature[] SERIALIZER_FEATURES_NULL_VALUE = { SerializerFeature.WriteMapNullValue, SerializerFeature.SortField,
                                                                                SerializerFeature.SkipTransientField };

    /** 反序列化属性 */
    private static final Feature[]           DE_SERIALIZER_FEATURES         = { Feature.AllowUnQuotedFieldNames, Feature.AllowSingleQuotes, Feature.InternFieldNames,
                                                                                Feature.AllowArbitraryCommas, Feature.IgnoreNotMatch };

    /** 日期类型默认序列化格式 */
    private static final String              DEFAULT_DATE_FORMAT            = "yyyy-MM-dd HH:mm:ss.SSS";

    /** 序列化配置 */
    private static final SerializeConfig     MAPPING                        = new SerializeConfig();

    static {
        MAPPING.put(Date.class, new SimpleDateFormatSerializer(DEFAULT_DATE_FORMAT));
    }

    /**
     * 构造函数
     */
    private FastJsonUtils() {
    }

    /**
     * java对象序列化为字符串
     *
     * @param obj 待序列化的java对象
     * @return
     */
    public static String toJSONString(Object obj) {
        return toJSONString(obj, null);
    }

    /**
     * Java对象转换为JSON字符串
     *
     * @param obj Java对象
     * @param dateFormat 日期格式
     * @return 序列化后的JSON字符串
     */
    public static String toJSONString(Object obj, String dateFormat) {
        if (StringUtils.isBlank(dateFormat)) {
            return JSON.toJSONString(obj, MAPPING, SERIALIZER_FEATURES);
        }
        return JSON.toJSONStringWithDateFormat(obj, dateFormat, SERIALIZER_FEATURES);
    }

    /**
     * java对象序列化为字符串
     *
     * @param obj 待序列化的java对象
     * @return
     */
    public static String toJSONStringNullValue(Object obj) {
        return toJSONStringNullValue(obj, null);
    }

    /**
     * Java对象转换为JSON字符串，空值输出，适用某些日志的原样输出的场景
     *
     * @param obj obj
     * @param dateFormat 日期格式
     * @return {@link String}
     */
    public static String toJSONStringNullValue(Object obj, String dateFormat) {
        if (StringUtils.isBlank(dateFormat)) {
            return JSON.toJSONString(obj, MAPPING, SERIALIZER_FEATURES_NULL_VALUE);
        }
        return JSON.toJSONStringWithDateFormat(obj, dateFormat, SERIALIZER_FEATURES_NULL_VALUE);
    }

    /**
     * Java对象转换为二进制数据
     *
     * @param obj Java对象
     * @return 序列化后的JSON二进制数据
     */
    public static byte[] toJSONStringBytes(Object obj) {
        return toJSONStringBytes(obj, null);
    }

    /**
     * Java对象转换为二进制数据
     *
     * @param obj Java对象
     * @param dateFormat 日期格式
     * @return 序列化后的JSON字符串
     */
    public static byte[] toJSONStringBytes(Object obj, String dateFormat) {
        if (!StringUtils.isBlank(dateFormat)) {
            MAPPING.put(Date.class, new SimpleDateFormatSerializer(dateFormat));
        }
        return JSON.toJSONBytes(obj, MAPPING, SERIALIZER_FEATURES);
    }

    /**
     * JSON字符串反序列化为Java对象
     *
     * @param json JSON字符串
     * @param clazz 反序列化对象类型
     * @return 反序列化对象
     */
    public static <T> T fromJSONString(String json, Class<T> clazz) {
        return JSON.parseObject(json, clazz, DE_SERIALIZER_FEATURES);
    }

    /**
     * JSON字符串反序列化为Java对象
     *
     * @param json JSON字符串
     * @param clazz 反序列化对象类型
     * @return 反序列化对象
     */
    public static <T> List<T> fromJSON2List(String json, Class<T> clazz) {
        return JSON.parseArray(json, clazz);
    }

    /**
     * JSON字符串反序列化为Java对象
     *
     * @param json JSON字符串
     * @param type 反序列化对象类型
     * @return 反序列化对象
     */
    public static <T> T fromJSONString(String json, Type type) {
        return JSON.parseObject(json, type, DE_SERIALIZER_FEATURES);
    }

    /**
     * JSON字符串反序列化为Java对象
     * @param json JSON字符串
     * @param clazz 反序列化对象类型
     * @param dateFormat 日期格式
     * @return 反序列化对象
     */
    public static <T> T fromJSONString(String json, Class<T> clazz, String dateFormat) {
        if (StringUtils.isNotBlank(dateFormat)) {
            ParserConfig config = new ParserConfig();
            config.putDeserializer(Date.class, new DateFormatDeserializer(dateFormat));
            return JSON.parseObject(json, clazz, config, JSON.DEFAULT_PARSER_FEATURE, DE_SERIALIZER_FEATURES);
        }
        return JSON.parseObject(json, clazz, DE_SERIALIZER_FEATURES);
    }

    /**
     * JSON字符串反序列化为Java对象
     *
     * @param json JSON字符串
     * @param ref 反序列化对象类型
     * @return 反序列化对象
     */
    public static <T> T fromJSONString(String json, TypeReference<T> ref) {
        return JSON.parseObject(json, ref, DE_SERIALIZER_FEATURES);
    }

    /**
     *  JSON字符串反序列化为Java对象
     *
     * @param json JSON字符串
     * @param ref 反序列化对象类型
     * @param dateFormat 日期格式
     * @return 反序列化对象
     */
    public static <T> T fromJSONString(String json, TypeReference<T> ref, String dateFormat) {
        if (StringUtils.isNotBlank(dateFormat)) {
            ParserConfig config = new ParserConfig();
            config.putDeserializer(Date.class, new DateFormatDeserializer(dateFormat));
            return JSON.parseObject(json, ref.getType(), config, JSON.DEFAULT_PARSER_FEATURE, DE_SERIALIZER_FEATURES);
        }
        return JSON.parseObject(json, ref, DE_SERIALIZER_FEATURES);
    }

    /**
     * JSON二进制数据反序列化为Java对象
     *
     * @param bytes JSON二进制数据
     * @param clazz 反序列化对象类型
     * @return 反序列化对象
     */
    public static <T> T fromJSONString(byte[] bytes, Class<T> clazz) {
        return JSON.<T> parseObject(bytes, clazz, DE_SERIALIZER_FEATURES);
    }

    /**
     * JSON二进制数据反序列化为Java对象
     *
     * @param bytes JSON二进制数据
     * @param clazz 反序列化对象类型
     * @param dateFormat 日期格式
     * @return 反序列化对象
     */
    public static <T> T fromJSONString(byte[] bytes, Class<T> clazz, String dateFormat) {
        return JSON.parseObject(bytes, clazz, DE_SERIALIZER_FEATURES);
    }

    /**
     * JSON二进制数据反序列化为Java对象
     *
     * @param bytes JSON二进制数据
     * @param off 二进制数据起始字节数组下标
     * @param len 需要反序列化二进制数据的长度
     * @param charsetDecoder 反序列化数据的字符编码类型
     * @param clazz 反序列化对象类型
     * @return 反序列化对象
     */
    public static <T> T fromJSONString(byte[] bytes, int off, int len, CharsetDecoder charsetDecoder, Class<T> clazz) {
        return JSON.<T> parseObject(bytes, off, len, charsetDecoder, clazz, DE_SERIALIZER_FEATURES);
    }

    /**
     * JSON字符串简单添加Entry功能，只能再第一次数据添加key-value，不支持嵌套类型
     * @param json JSON字符串
     * @param key 添加的Entry key
     * @param value 添加的Entry value
     * @param addDelimiter 是否需要添加逗号分隔符
     * @return 添加Entry后的JSON字符串
     */
    public static String addEntry(String json, String key, String value, boolean addDelimiter) {
        StringBuilder buff = new StringBuilder();
        buff.append("{").append("\"").append(key).append("\"").append(":").append("\"").append(value).append("\"");
        if (addDelimiter) {
            buff.append(",");
        }
        buff.append(json.substring(1));
        return buff.toString();
    }
}
